<?php 
namespace town\base;

use town\base\Filter;

class FilterChain
{
	private static $obj = null;
    private $filters = null;
    private $index   = null;

    public function __construct()
    {
        $this->filters = [];
        $this->index   = -1;
    }

    private function init($config = [])
    {
    	if (is_array($config)){
    		$this->filters = $config;
        } else {
            $this->filters[] = $config;
        }
    }

    private function addFilter($filter)
    {
        if (is_array($filter)) {
            $this->filters = array_merge($this->filters, $filter);
        } else {
            $this->filters[] = $filter;
        }
    }

    private function doFilter($request)
    {
        $this->index++;
        // 执行完了结束
        if ($this->index == count($this->filters)) {
            $this->distory();
            return $this->index;
        }
        
        // 是闭包结束
        $filter = $this->filters[$this->index];
        if (is_callable($filter)) {
        	return $filter($request);
        }
        
        // 返回响应对象,向下一个过滤器执行
        $ref_object = new \ReflectionClass('town\\filter\\'.$filter);
        $object   = $ref_object->newInstance();
        $response = call_user_func([$object, 'doFilter'], $request, [$this, 'doFilter']);
        if (is_callable($response)) {
        	return $this->doFilter($request);
        }
        
        // 没有返回结果结束
        return $response;
    }

    private function distory()
    {
        unset($this->filters);
    }
    
    public function __call($method, $arguments)
    {
    	if (method_exists($this, $method)) {
    		return call_user_func_array([$this, $method], $arguments);
    	}
    }
    
    public static function __callStatic($method, $arguments)
    {
    	$obj = static::singleton();
    	if (method_exists($obj, $method)) {
    		return call_user_func_array([$obj, $method], $arguments);
    	}
    }
    
    public static function singleton()
    {
    	if (static::$obj == null) {
    		static::$obj = new static();
    	}
    	return static::$obj;
    }
}